package com.tmt.fileservice.impl;

import com.tmt.annotation.parser.LazyLoadParser;
import com.tmt.fileservice.FileSaveMode;
import com.tmt.fileservice.IFileDispatcher;
import com.tmt.helper.ReflectHelper;
import com.tmt.helper.StringHelper;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.StringUtils;

import javax.persistence.Id;
import java.io.File;
import java.text.MessageFormat;

/**
 * Created by ThreeManTeam on 2017/5/25.
 */

@Getter
@Setter
public class DefaultFileDispatcher implements IFileDispatcher {

    /**
     * 文件路径格式：{基本URL}/{存储目录}/{类名}/{ID}/{属性名}/{文件名后缀}/{文件名}
     */
    private static final String URL_FORMAT = "/{0}/{1}/{2}/{3}/{4}/{5}/{6}";

    /**
     * 文件路径格式：{基本路径}/{存储目录}/{类名}/{ID}/{属性名}/{文件名}.{文件名后缀}
     */
    private static final String PATH_FORMAT = "{0}/{1}/{2}/{3}/{4}/{5}.{6}";

    /**
     * 带盘符的，保存文件的基本目录。
     */
    private String baseDir;

    /**
     * 在BasePath下的保存临时文件的目录名称。
     */
    private String tmpDir;

    /**
     *在BasePath下的保存持久化文件的目录名称。
     */
    private String mainDir;

    /**
     * 访问文件的基本URL.
     */
    private String baseUrl;

    /**
     * 即Server.context-path参数。
     */
    private String contextPath = "/";

    /**
     * 主机地址。可以是IP地址或域名, 默认为localhost。
     */
    private String hostAddress = "localhost";

    /**
     * 主机端口号，默认为80端口
     */
    private int port = 80;

    /**
     * 使用的协议，http或https
     */
    private String protocol;

    /**
     * 临时文件过期时间，单位：秒。 为0表示永不过期。
     */
    private long fileExpiredTime = 300L;


    private static DefaultFileDispatcher instance;

    private DefaultFileDispatcher(String protocol, String hostAddress, int port,
                                  String contextPath, String baseUrl, String baseDir,
                                  String mainDir, String tmpDir, long fileExpiredTime){
        this.protocol = protocol;
        this.hostAddress = hostAddress;
        this.port = port;
        this.contextPath = contextPath;
        this.baseUrl = baseUrl;
        this.baseDir = baseDir;
        this.mainDir = mainDir;
        this.tmpDir = tmpDir;
        this.fileExpiredTime = fileExpiredTime > 0 ? fileExpiredTime : 0;
    }

    public synchronized static DefaultFileDispatcher getInstance(){
        if (instance == null) {
            instance = new DefaultFileDispatcher("http", "localhost", 80,
                    "/", "filestore", "c:/filestore",
                    "/main", "/tmp", 300L);
        }
        return instance;
    }

    public synchronized static DefaultFileDispatcher getInstance(String protocol, String hostAddress, int port,
                                                                 String contextPath, String baseUrl,
                                                                 String basePath, String mainDir,
                                                                 String tmpDir, long fileExpiredTime){
        if (instance == null) {
            instance = new DefaultFileDispatcher(protocol, hostAddress, port,
                    contextPath, baseUrl, basePath, mainDir, tmpDir, fileExpiredTime);
        }
        return instance;
    }

    @Override
    public <T> String getFilePath(T obj, String propertyName, String defaultPropertyValue, FileSaveMode fileSaveMode) {
        if (obj == null || StringHelper.isEmpty(propertyName))
            return null;

        String idPath = "0";
        Object fileUrl = !StringHelper.isEmpty(defaultPropertyValue) ? defaultPropertyValue : (propertyName + ".jpg");
        if (!obj.getClass().isAssignableFrom(Class.class)){
            Object idValue = ReflectHelper.getFieldValueByAnnotation(obj, Id.class, "0");
            idPath = idValue.toString();
            Object tmpFileUrl = ReflectHelper.getFieldValue(obj, propertyName);
            if (!StringUtils.isEmpty(tmpFileUrl))
                fileUrl = tmpFileUrl;
        }

        String[] fileNames = getFileName(fileUrl.toString());
        String pathInfo = fileSaveMode == FileSaveMode.TEMPARARY ? tmpDir : mainDir;

        String clazzName = LazyLoadParser.getValidSimpleClassName(obj.getClass());
        if (obj instanceof Class<?>)
            clazzName = ((Class<?>)obj).getSimpleName();

        String filePath = MessageFormat.format(PATH_FORMAT, baseDir, pathInfo, clazzName, idPath, propertyName, fileNames[0], fileNames[1]);
        return filePath.replaceAll("//", "/");
    }

    @Override
    public <T> String getNewFilePath(Class<T> clazz, Object idValue, String propertyPath, String propertyValue, FileSaveMode fileSaveMode) {
        if (clazz == null || StringHelper.isEmpty(propertyPath) || StringHelper.isEmpty(propertyValue))
            return null;

        String idPath = fileSaveMode == FileSaveMode.TEMPARARY ? "0" : idValue.toString();
        if (StringHelper.isEmpty(idPath))
            return null;

        Class tagetClazz = ReflectHelper.getActualClassByFieldPath(clazz, propertyPath, false, true);

        String[] infos = propertyPath.replaceAll("\\[\\]", "").split("\\.");
        String propertyName = infos[infos.length - 1];

        String[] fileNames = getFileName(propertyValue);
        String pathInfo = fileSaveMode == FileSaveMode.TEMPARARY ? tmpDir : mainDir;
        propertyName = propertyName.replaceAll("\\[\\]\\.", "_");
        String filePath = MessageFormat.format(PATH_FORMAT, baseDir, pathInfo, tagetClazz.getSimpleName(), idPath, propertyName, fileNames[0], fileNames[1]);
        return filePath.replaceAll("//", "/");
    }

    @Override
    public <T> String getFileUrl(T obj, String propertyName, FileSaveMode fileSaveMode) {
        Object idValue = ReflectHelper.getFieldValueByAnnotation(obj, Id.class, "0");
        String idPath = idValue.toString();

        Object filePath = ReflectHelper.getFieldValue(obj, propertyName);
        if (filePath == null)
            return null;

        String pathInfo = fileSaveMode == FileSaveMode.TEMPARARY ? tmpDir : mainDir;

        String[] fileNames = getFileName(filePath.toString());
        String fileUrl = MessageFormat.format(URL_FORMAT, getValidBaseUrl(), pathInfo,
                LazyLoadParser.getValidSimpleClassName(obj.getClass()),
                idPath, propertyName, fileNames[1], fileNames[0]);
        fileUrl = fileUrl.replaceAll("//", "/");

        if (fileSaveMode == FileSaveMode.PERSIST){
            //确保MainDir目录下存在目标文件？
            // 如果不存在，则到临时目录中去查找；如果找到，则将文件拷贝到MainDir相应的目录中。
            String tmpFilePath = MessageFormat.format(PATH_FORMAT, baseDir, pathInfo,
                    LazyLoadParser.getValidSimpleClassName(obj.getClass()),
                    idPath, propertyName, fileNames[0], fileNames[1]);
            tmpFilePath = tmpFilePath.replaceAll("//", "/");
            tryMoveFileToMainDir(tmpFilePath);
        }

        return getHostUrl() +fileUrl;
    }

    @Override
    public String fileUrlToPath(String fileUrl) {
        String address = getHostUrl();
        fileUrl = fileUrl.replace(address, "");

        String[] fileNames = getFileName(fileUrl);
        fileUrl = StringHelper.splitWithoutEmpty(fileUrl, fileNames[1])[0];

        fileUrl = fileUrl.replaceFirst(getValidBaseUrl(),"");
        fileUrl = fileUrl.replaceAll("//", "/");

        String filePath = MessageFormat.format("{0}/{1}/{2}.{3}", baseDir, fileUrl, fileNames[0], fileNames[1]);
        return filePath.replaceAll("//", "/");
    }

    @Override
    public String filePathToUrl(String filePath) {
        String[] fileNames = getFileName(filePath);
        filePath = StringHelper.splitWithoutEmpty(filePath, fileNames[0])[0];

        filePath = filePath.replaceFirst(baseDir,"");
        filePath = filePath.replaceAll("//", "/");

        String fileUrl = MessageFormat.format("{0}/{1}/{2}/{3}", getValidBaseUrl(), filePath, fileNames[1], fileNames[0]);
        return getHostUrl() + fileUrl.replaceAll("//", "/");
    }

    @Override
    public <T> String getValidatorName(T obj, String propertyName) {
        return MessageFormat.format("{0}#{1}", LazyLoadParser.getValidSimpleClassName(obj.getClass()), propertyName);
    }

    @Override
    public String getValidatorNameByPath(String filePath) {
        filePath = filePath.replaceFirst(baseDir, "");
        String infos[] = StringHelper.splitWithoutEmpty(filePath, "/");
        return MessageFormat.format("{0}#{1}", infos[1], infos[3]);
    }

    @Override
    public String getValidatorNameByUrl(String fileUrl) {
        String filePath = fileUrlToPath(fileUrl);
        return getValidatorNameByPath(filePath);
    }

    @Override
    public String getTmpFileDir() {
        return this.baseDir + this.tmpDir;
    }

    @Override
    public long getFileExpiredTime() {
        return fileExpiredTime;
    }

    @Override
    public String getContextPath() {
        return contextPath;
    }

    @Override
    public String[] getFileName(String fileUrlOrPath){
        String[] info = fileUrlOrPath.split(baseUrl);
        String path = info.length > 1 ? info[1] : info[0];
        if (path.contains(".")){
            String[] infos = StringHelper.splitWithoutEmpty(path, "/");
            infos = StringHelper.splitWithoutEmpty(infos[infos.length - 1], "\\.");
            return new String[]{infos[infos.length - 2], infos[infos.length - 1]};
        }

        String[] infos = StringHelper.splitWithoutEmpty(path, "/");
        return new String[]{infos[infos.length - 1], infos[infos.length - 2]};
    }

    @Override
    public String getHostUrl(){
        String tmpProtocol = (protocol == null || protocol.trim().isEmpty()) ? "http" : protocol;
        String address = (hostAddress == null || hostAddress.trim().isEmpty()) ? "localhost" : hostAddress;
        return MessageFormat.format("{0}://{1}:{2}", tmpProtocol, address, "" + port);
    }

    public void tryMoveFileToMainDir(String filePath){
        File file = new File(filePath);
        if (file.exists())
            return;

        String tmpMainDir = ("/"+ mainDir + "/").replaceAll("//", "/");
        String tmpTmpDir = ("/"+ tmpDir + "/").replaceAll("//", "/");
        String tmpFilePath = filePath.replaceFirst(tmpMainDir, tmpTmpDir);

        file = new File(tmpFilePath);
        if (file.exists()){
            //将文件移动到filePath位置。
            file.renameTo(new File(filePath));
            return;
        }

        String path = tmpFilePath.replaceFirst(baseDir, "");
        String[] infos = StringHelper.splitWithoutEmpty(path, "/");

        String idValue = "/" + infos[2] + "/";
        tmpFilePath = tmpFilePath.replaceFirst(idValue, "/0/");

        file = new File(tmpFilePath);
        if (file.exists()){
            //将文件移动到filePath位置。
            File tagetFile = new File(filePath);
            if (!tagetFile.getParentFile().exists())
                tagetFile.getParentFile().mkdirs();
            file.renameTo(new File(filePath));
        }
    }

    private String getValidBaseUrl(){
        return (contextPath == null || contextPath.trim().isEmpty() || "/".equals(contextPath.trim())) ?
                baseUrl : contextPath + baseUrl;
    }

}
